<template>
  <!-- 滚动加载容器 -->
  <!-- 获取DOM引用 -->
  <!--动态设置容器高度-->
  <!--滚动事件监听-->
  <div
    ref="scrollContainer"
    class="scroll-container"
    :style="{ height: containerHeight }"
    @scroll="handleScroll"
  >
    <!-- 滚动内容区域 -->
    <div class="scroll-content">
      <!-- 默认插槽，用于放置用户传入的内容 -->
      <slot></slot>

      <!-- 加载状态指示器 -->
      <div v-if="loading" class="loading-indicator">
        <!-- 具名插槽，允许自定义加载提示 -->
        <slot name="loading">
          <van-loading vertical>
            <template #icon>
              <van-icon name="star-o" size="30" style="color: #31dfb0" />
            </template>
            加载中...
          </van-loading>
        </slot>
      </div>

      <!-- 加载完成指示器 -->
      <div v-if="finished" class="finished-indicator">
        <!-- 具名插槽，允许自定义完成提示 -->
        <slot name="finished">没有更多了</slot>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";

/**
 * 组件属性定义
 * withDefaults 为props提供默认值
 */
const props = withDefaults(
  defineProps<{
    // 容器高度 (必需)，如 "300px" 或 "50vh"
    containerHeight: string;
    // 是否正在加载中状态
    loading?: boolean;
    // 是否已全部加载完成
    finished?: boolean;
    // 触发加载的距离阈值(像素)，距离底部多少像素时触发
    threshold?: number;
  }>(),
  {
    loading: false, // 默认不在加载状态
    finished: false, // 默认未加载完成
    threshold: 20, // 默认距离底部20px触发
  }
);

/**
 * 组件事件定义
 * 只定义一个load事件，当触底时触发
 */
const emit = defineEmits<{
  (e: "load"): void; // 加载更多事件
}>();

// 获取滚动容器的DOM引用
const scrollContainer = ref<HTMLElement | null>(null);

/**
 * 处理滚动事件
 * 计算是否滚动到底部阈值范围内
 */
const handleScroll = () => {
  // 如果正在加载、已加载完成或容器不存在，则直接返回
  if (props.loading || props.finished || !scrollContainer.value) return;

  // 获取滚动容器相关尺寸信息
  const { scrollHeight, scrollTop, clientHeight } = scrollContainer.value;

  // 计算距离底部的距离
  const distanceToBottom = scrollHeight - (scrollTop + clientHeight);

  // 如果距离小于等于阈值，触发load事件
  if (distanceToBottom <= props.threshold) {
    emit("load");
  }
};

/**
 * 防抖处理
 * 避免滚动事件频繁触发导致性能问题
 */
let timer: number | null = null;
const debounceHandleScroll = () => {
  if (timer) clearTimeout(timer); // 清除之前的定时器
  timer = setTimeout(() => {
    handleScroll(); // 执行真正的滚动处理
    timer = null; // 重置定时器引用
  }, 100); // 100ms防抖间隔
};

/**
 * 使用ResizeObserver监听容器尺寸变化
 * 当内容高度变化时重新计算位置
 */
let observer: ResizeObserver | null = null;

// 组件挂载时
onMounted(() => {
  if (scrollContainer.value) {
    // 创建ResizeObserver实例，监听容器尺寸变化
    observer = new ResizeObserver(debounceHandleScroll);
    // 开始观察目标元素
    observer.observe(scrollContainer.value);
  }
});

// 组件卸载时
onUnmounted(() => {
  if (observer) {
    // 停止观察并清理
    observer.disconnect();
  }
});
</script>

<style scoped lang="less">
/* 滚动容器样式 */
.scroll-container {
  overflow-y: auto; /* 允许垂直滚动 */
  -webkit-overflow-scrolling: touch; /* iOS平滑滚动 */

  /* 隐藏滚动条 */
  scrollbar-width: none; /* Firefox */
  &::-webkit-scrollbar {
    display: none; /* Chrome/Safari */
  }
}

/* 滚动内容区域 */
.scroll-content {
  position: relative; /* 相对定位，为子元素定位提供基准 */
}

/* 加载中和加载完成指示器样式 */
.loading-indicator,
.finished-indicator {
  padding: 10px 0; /* 上下内边距 */
  text-align: center; /* 文字居中 */
  color: #999; /* 文字颜色 */
  font-size: 14px; /* 文字大小 */
}
</style>
